A Cloud Development Environment(CDE) is a type of software development workspace that runs in the cloud, rather than on your local computer. It allows you to write, run, and debug code entirely through a web browser from any device.
Background
Recently, my MacBook Pro has started having issues that sometimes prevent me from working. It's already eight years old and due for an upgrade. However, I'm waiting for Apple's Back to School promotion, which starts in June, before buying a new one. In the meantime, I need an alternative solution for the next few months. I do have an iPad with a keyboard, so I'm exploring ways to use it for coding.
Solutions
Initially, I set up a Linux system on the cloud and connected to it remotely using my iPad. However, I found the experience laggy and dependent on a high-speed internet connection. The pointer movement wasn’t smooth either, which made it difficult to work efficiently. Because of that, I switched to my current solution: using Code Server. It runs entirely in the browser, and with PWA support, it can even be installed as a standalone app on the iPad.
In addition to the IDE, I also need access to other essential development tools—such as database management, API testing, and version control—that work well in a browser environment on the iPad.
Item | App or Tools | Note |
---|---|---|
IDE | Code Server | Code on browsers like VS Code, most of the extensions are supported |
Database Manager | pgAdmin | Manage PostgreSQL database on browser |
SSH | Termius | Visit and manage VMs via iPad |
API testing | Posman | Postman browser version, not need to install the app |
Set up the CDE using Docker
I decided to use Docker to manage all the development tools because it's easy to maintain, replicate, and deploy across different environments.
1. Install docker and docker-compose
sudo apt update
sudo apt install docker.io docker-compose
sudo systemctl enable docker
sudo systemctl start docker
2. Install Code Server
version: '3'
services:
code-server:
build: .
container_name: code-server
restart: always
volumes:
- /root/project:/home/coder/project
- /root/docker/code-server/local:/home/coder/.local
- /root/.ssh:/home/coder/.ssh
- /root/.gitconfig:/home/coder/.gitconfig
environment:
- PASSWORD=[Your Password]
- GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no
expose:
- 8080
Besides the project
folder, I also created a local
folder to persist Code Server settings. This ensures that I don't lose my configuration when restarting the Docker container. I've also configured Code Server to use the same Git and SSH settings as my VM instance, so the development environment stays consistent.
To optimize Code Server for development, I built a custom Dockerfile that installs Node.js, Zsh, and other essential tools, so everything I need is pre-configured and ready to use.
Dockerfile:
FROM ghcr.io/coder/code-server:latest
USER root
# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Specify the zsh as default command
RUN chsh -s /usr/bin/zsh coder
USER coder
# Install Oh-My-Ssh
RUN RUNZSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# Create the .zshrc file
RUN echo 'export ZSH="$HOME/.oh-my-zsh"\n\
ZSH_THEME="robbyrussell"\n\
plugins=(\n\
z\n\
)\n\
source $ZSH/oh-my-zsh.sh\n\
\n\
alias zshconfig="vim ~/.zshrc"\n\
alias gs="git status"\n\
alias gc="git checkout"\n\
alias gcm="git add -A; git commit -am"\n\
alias gpusho="git push origin"\n\
alias gpullo="git pull origin"\n\
' > /home/coder/.zshrc
WORKDIR /home/coder/project
3. Install PostgreSQL and pgAdmin
version: '3'
services:
postgres:
image: postgres
container_name: postgres
restart: always
environment:
- POSTGRES_DB=[Your DB]
- POSTGRES_PASSWORD=[Your password]
volumes:
- postgres_data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin
restart: always
environment:
- PGADMIN_DEFAULT_EMAIL=[Your Email]
- PGADMIN_DEFAULT_PASSWORD=[Your Password]
volumes:
- pgadmin_data:/var/lib/pgadmin
expose:
- 80
depends_on:
- postgres
volumes:
postgres_data:
pgadmin_data:
4. Install Caddy
Caddy is a simpler alternative to Nginx for setting up a reverse proxy. However, if you don’t have a domain or don’t need to access the server via a domain name, you can skip this step and visit the server using the public IP directly.
version: '3'
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- code-server
- pgadmin
volumes:
caddy_data:
caddy_config:
Next, I created a Caddyfile
to configure the reverse proxy. This allows us to use a subdomain for accessing the different services.
[Your Domain 1] {
reverse_proxy code-server:8080
}
[Your Domain 2] {
reverse_proxy pgadmin:80
}
Here is the whole docker-compose.yml
configuration.
services:
code-server:
build: .
container_name: code-server
restart: always
volumes:
- /root/project:/home/coder/project
- /root/docker/code-server/local:/home/coder/.local
- /root/.ssh:/home/coder/.ssh
- /root/.gitconfig:/home/coder/.gitconfig
environment:
- PASSWORD=[Your Password]
- GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no
expose:
- 8080
postgres:
image: postgres
container_name: postgres
restart: always
environment:
- POSTGRES_DB=[Your DB nae]
- POSTGRES_PASSWORD=[Your Password]
volumes:
- postgres_data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin
restart: always
environment:
- PGADMIN_DEFAULT_EMAIL=[Your Email]
- PGADMIN_DEFAULT_PASSWORD=[Your Password]
volumes:
- pgadmin_data:/var/lib/pgadmin
expose:
- 80
depends_on:
- postgres
caddy:
image: caddy:2-alpine
container_name: caddy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- code-server
- pgadmin
volumes:
caddy_data:
caddy_config:
postgres_data:
pgadmin_data:
Once the Docker services have been started, you can visit the Code Server and pgAdmin via browsers.
Any server running inside Code Server can be accessed through a proxy path in the format: {code-server-domain}/proxy/{port}
. For example, if a server previously ran locally on port 5000
, instead of visiting it at localhost:5000
, you would now access it via https://your-code-server-domain/proxy/5000
.
You won't be able to access any server running inside Code Server without being logged in first, so make sure to sign in before testing. If you're using Postman to test an API, you'll also need to include the code-server-session
cookie in your requests. Fortunately, Postman offers a browser extension called Postman Interceptor, which makes it easy to sync cookies directly from your browser to Postman.
Summary
After using Code Server for several months, I'm quite satisfied with it. It is almost identical to running VS Code locally, and in some cases, it's even more efficient, especially when working with APIs that require an HTTPS address, which localhost
doesn't provide.
However, it's not well-suited for mobile Application development. You can't simulate a phone on this platform, and if you're developing iOS apps, the experience is even more limited since Xcode is required and required and only runs on macOS. Additionally, if the project includes a large number of binary files, such as images, it won't be shown immediately because it's a browser-based environment and needs time to load.